﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEditor.AddressableAssets;
using UnityEditor.AddressableAssets.Settings;
using UnityEditor.SceneManagement;
using UnityEngine;

namespace Editor.Gmx.AutoGroup
{
    [InitializeOnLoad]
    public class AutoGroupImporter : AssetPostprocessor
    {
        private static List<string> sNeedReImport = new();

        // The selection active object 
        private static UnityEngine.Object sSelectionActiveObject = null;

        static AutoGroupImporter()
        {
            Selection.selectionChanged += OnSelectionChanged;
        }

        private static void OnSelectionChanged()
        {
            sSelectionActiveObject = Selection.activeObject;
        }


        private static bool CheckResource()
        {
            var autoSettings = AutoGroupSetting.Instance;
            if (autoSettings == null)
            {
                Debug.LogError("AutoGroupSetting 不存在");
                return false;
            }

            var settings = AddressableAssetSettingsDefaultObject.Settings;
            if (settings == null)
            {
                var asf = Assembly.Load("Unity.Addressables.Editor");
                var aSettings = asf.GetType(" UnityEditor.AddressableAssets.Settings.AddressableAssetUtility");
                MethodInfo convertAsset =
                    aSettings.GetMethod("ConvertAssetBundlesToAddressables",
                        BindingFlags.Static | BindingFlags.NonPublic);
                if (convertAsset == null)
                {
                    return false;
                }

                convertAsset.Invoke(null, null);
                return false;
            }

            return true;
        }

        private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets,
            string[] movedAssets, string[] movedFromAssetPaths)
        {
            if (!CheckResource())
            {
                return;
            }

            Debug.Log("[AutoGroup] Start AutoGroupImporter.OnPostprocessAllAssets");
            var settings = AddressableAssetSettingsDefaultObject.Settings;
            var autoSettings = AutoGroupSetting.Instance;

            var cachedSelectionActiveObject = sSelectionActiveObject;
            var dirty = false;
            var prefabStage = PrefabStageUtility.GetCurrentPrefabStage(); //当前编辑的预制体
#if UNITY_2020_1_OR_NEWER
            var prefabAssetPath = prefabStage != null ? prefabStage.assetPath : null;
#else
            string prefabAssetPath = prefabStage != null ? prefabStage.prefabAssetPath : null;
#endif

            try
            {
                for (var i = 0; i < importedAssets.Length; i++)
                {
                    var importedAsset = importedAssets[i];
                    if (autoSettings._showImportProgressBar
                        && EditorUtility.DisplayCancelableProgressBar(
                            "Auto Group import settings", $"[{i}/{importedAssets.Length}] {importedAsset}",
                            (float)i / importedAssets.Length))
                    {
                        break;
                    }

                    if (autoSettings.IsAssetIgnored(importedAsset))
                    {
                        continue;
                    }

                    dirty |= AutoGroupRule.ApplyImportRule(importedAsset, null, settings, autoSettings);
                }
            }
            finally
            {
                EditorUtility.ClearProgressBar();
            }

            for (var i = 0; i < movedAssets.Length; i++)
            {
                var movedAsset = movedAssets[i];
                if (autoSettings.IsAssetIgnored(movedAsset))
                    continue;
                var movedFromAssetPath = movedFromAssetPaths[i];
                // Ignore current editing prefab asset.
                if (prefabStage == null || prefabAssetPath != movedAsset)
                {
                    dirty |= AutoGroupRule.ApplyImportRule(movedAsset, movedFromAssetPath, settings, autoSettings);
                }
            }

            foreach (var deletedAsset in deletedAssets)
            {
                if (autoSettings.IsAssetIgnored(deletedAsset))
                    continue;
                var guid = AssetDatabase.AssetPathToGUID(deletedAsset);
                if (!string.IsNullOrEmpty(guid) && settings.RemoveAssetEntry(guid))
                {
                    dirty = true;
                    Debug.LogFormat("[AutoGroup] Entry removed for {0}", deletedAsset);
                }
            }

            if (dirty)
            {
                //删除不需要的MissingGroup
                RemoveMissGroup();
                //删除不需要的标签
                DelUnUsedLables();
                //自动检测重复的address和存在空格名字的资源
                CheckAutoGroup();
                //如果打开了group界面 需要关闭 因为在修改entry的数据代码没有在OnGUI 中会触发报错
                CloseAddressWindow();
                //当对单个文件进行改名时 因为InspectorGUI存在缓存会报错 需要清理AddressableAssetInspectorGUI对于AddressableEntry的缓存
                ClearAddressableGUICache();
            }

            //如果修改的是规则文本 需要重新导入一下受到影响的文件   
            CheckRimportRuleFiles(importedAssets);
            Debug.Log("[AutoGroup] End AutoGroupImporter.OnPostprocessAllAssets");
            AssetDatabase.SaveAssets();
            Selection.activeObject = cachedSelectionActiveObject;
        }

        [MenuItem("Gmx/AutoGroup/CheckAutoGroup")]
        public static void CheckAutoGroup()
        {
            if (!CheckResource())
            {
                return;
            }

            Debug.Log("[AutoGroup] Start AutoGroupImporter.CheckAutoGroup");
            var settings = AddressableAssetSettingsDefaultObject.Settings;
            var autoSettings = AutoGroupSetting.Instance;
            autoSettings.SyncSettings();
            List<AddressableAssetEntry> entries = new List<AddressableAssetEntry>();
            settings.GetAllAssets(entries, false, (group) => { return group.name == autoSettings._autoGroupName; });
            Dictionary<string, AddressableAssetEntry> addressMap = new Dictionary<string, AddressableAssetEntry>();
            foreach (var entry in entries)
            {
                AddressableAssetEntry existEntry = null;
                if (addressMap.TryGetValue(entry.address, out existEntry))
                {
                    if (!entry.AssetPath.EndsWith(".shader"))
                    {
                        Debug.LogError(
                            $"[AutoGroup] 资源名重复 请更改资源名 pathA:{entry.AssetPath} pathB:{existEntry.AssetPath}");
                    }
                }
                else
                {
                    addressMap.Add(entry.address, entry);
                }

                if (Path.GetFileName(entry.AssetPath).Contains(" "))
                {
                    if (!entry.AssetPath.EndsWith(".shader"))
                    {
                        Debug.LogError($"[AutoGroup] 资源名存在空格 请修复 address:{entry.AssetPath}");
                    }
                }
            }

            Debug.Log("[AutoGroup] End AutoGroupImporter.CheckAutoGroup");
        }

        [MenuItem("Gmx/AutoGroup/ClearAddressableGUICache")]
        public static void ClearAddressableGUICache()
        {
            var asf = Assembly.Load("Unity.Addressables.Editor");
            var type = asf.GetType("UnityEditor.AddressableAssets.GUI.AddressableAssetInspectorGUI");
            FieldInfo cacheField =
                type.GetField("s_Cache", BindingFlags.Static | BindingFlags.NonPublic);
            if (cacheField != null)
            {
                // 通过反射获取 s_Cache 字段的值
                var cacheValue = cacheField.GetValue(null);
                if (cacheValue != null)
                {
                    Type cacheType = cacheValue.GetType();
                    // 获取 m_TargetInfoCache 字段的值
                    FieldInfo targetInfoCacheField = cacheType.GetField("m_TargetInfoCache",
                        BindingFlags.Instance | BindingFlags.NonPublic);
                    if (targetInfoCacheField != null)
                    {
                        var targetInfo = targetInfoCacheField.GetValue(cacheValue);
                        if (targetInfo is IDictionary targetInfoCacheDictionary)
                        {
                            targetInfoCacheDictionary.Clear(); // 清空 m_TargetInfoCache 字典
                            Debug.Log("[AutoGroup]: AddressableAssetInspectorGUI Cache Clear");
                        }
                    }
                }
            }
        }

        [MenuItem("Gmx/AutoGroup/DelMissGroup")]
        public static void RemoveMissGroup()
        {
            var settings = AddressableAssetSettingsDefaultObject.Settings;
            var groups = settings.groups;
            if (groups.Count > 2)
            {
                int index = -1;
                foreach (AddressableAssetGroup assetGroup in groups)
                {
                    index++;
                    if ((assetGroup == null) || (assetGroup.Name != "AutoGroup" && assetGroup.Name != "Built In Data"))
                    {
                        break;
                    }
                }

                groups.RemoveAt(index);
            }
        }


        [MenuItem("Gmx/AutoGroup/DelUnUsedLabels")]
        public static void DelUnUsedLables()
        {
            if (!CheckResource())
            {
                return;
            }

            var settings = AddressableAssetSettingsDefaultObject.Settings;
            var autoSettings = AutoGroupSetting.Instance;
            HashSet<string> usedLabels = new HashSet<string>();
            var groups = settings.groups;
            foreach (AddressableAssetGroup assetGroup in groups)
            {
                foreach (AddressableAssetEntry entry in assetGroup.entries)
                {
                    foreach (string label in entry.labels)
                    {
                        if (autoSettings.IsRuleLable(label))
                        {
                            usedLabels.Add(label);
                        }
                    }
                }
            }

            HashSet<string> unused = new HashSet<string>(settings.GetLabels());
            var allRuleLables = unused.Where(l => autoSettings.IsRuleLable(l)).ToList();
            allRuleLables.RemoveAll(l => usedLabels.Contains(l));
            if (allRuleLables.Count > 0)
            {
                foreach (string s in allRuleLables)
                {
                    settings.RemoveLabel(s, false);
                }
            }

            Debug.Log("[AutoGroup]: Unused labels removed successfully.");
        }

        [MenuItem("Gmx/AutoGroup/CloseAddressWindow")]
        public static void CloseAddressWindow()
        {
            var asf = Assembly.Load("Unity.Addressables.Editor");
            var type = asf.GetType("UnityEditor.AddressableAssets.GUI.AddressableAssetsWindow");
            if (type != null)
            {
                var window = EditorWindow.GetWindow(type);
                if (window != null)
                {
                    window.Close();
                    Debug.Log("[AutoGroup]: AddressableAssetsWindow Close");
                }
            }
        }

        private static void CheckRimportRuleFiles(string[] importedAssets)
        {
            if (!CheckResource())
            {
                return;
            }

            var settings = AddressableAssetSettingsDefaultObject.Settings;
            var autoSettings = AutoGroupSetting.Instance;
            if (importedAssets.Length == 1)
            {
                sNeedReImport.Clear();
                for (var i = 0; i < importedAssets.Length; i++)
                {
                    var importedAsset = importedAssets[i];
                    if (autoSettings.IsSignFile(importedAsset))
                    {
                        string path = Path.GetDirectoryName(importedAsset);
                        string[] files = Directory.GetFiles(path, "*.*", SearchOption.TopDirectoryOnly);
                        foreach (var file in files)
                        {
                            if (!autoSettings.IsAssetIgnored(file) && !autoSettings.IsSignFile(file))
                            {
                                sNeedReImport.Add(file);
                            }
                        }
                    }
                }

                if (sNeedReImport.Count > 0)
                {
                    OnPostprocessAllAssets(sNeedReImport.ToArray(), new string[0], new string[0], new string[0]);
                    Debug.Log("[AutoGroup]: Reimport Success");
                }
            }
        }


        [MenuItem("Assets/AutoGroup: Check Folder(s)")]
        private static void CheckFoldersFromSelection()
        {
            DelShaderFromGroup();

            var autoSettings = AutoGroupSetting.Instance;
            if (autoSettings == null) return;
            var assetPaths = new List<string>();
            foreach (var obj in Selection.GetFiltered(typeof(UnityEngine.Object), SelectionMode.Assets))
                if (autoSettings.IsAssetInRoot(AssetDatabase.GetAssetPath(obj)))
                {
                    var assetPath = AssetDatabase.GetAssetPath(obj);
                    if (Directory.Exists(assetPath)) assetPaths.Add(assetPath);
                }

            ReimportFolders(assetPaths, autoSettings);
        }

        [MenuItem("Assets/AutoGroup: Check Folder(s)", true)]
        private static bool ValidateCheckFoldersFromSelection()
        {
            var autoSettings = AutoGroupSetting.Instance;
            if (autoSettings == null) return false;
            foreach (var obj in Selection.GetFiltered(typeof(UnityEngine.Object), SelectionMode.Assets))
                if (Directory.Exists(AssetDatabase.GetAssetPath(obj)) &&
                    autoSettings.IsAssetInRoot(AssetDatabase.GetAssetPath(obj)))
                    return true;
            return false;
        }


        [MenuItem("Gmx/Auto/Test/DelShaderFromGroup")]
        public static void DelShaderFromGroup()
        {
            var settings = AddressableAssetSettingsDefaultObject.Settings;
            var autoSettings = AutoGroupSetting.Instance;
            HashSet<string> delShaderGuidSet = GetShaderGuidSet(settings, autoSettings);
            foreach (var delGuid in delShaderGuidSet)
            {
                settings.RemoveAssetEntry(delGuid);
            }
        }


        [MenuItem("Gmx/Auto/Test/AddShaderToGroup")]
        public static void AddShaderToGroup()
        {
            var settings = AddressableAssetSettingsDefaultObject.Settings;
            var autoSettings = AutoGroupSetting.Instance;
            var autoGroup = settings.groups.Find(g => string.Equals(g.Name, autoSettings._autoGroupName));

            HashSet<string> shaderGuidSet = GetShaderGuidSet(settings, autoSettings);
            //删除列表中的shader资源
            foreach (var delGuid in shaderGuidSet)
            {
                settings.RemoveAssetEntry(delGuid);
            }

            //添加引用的shader资源到hashset
            foreach (var assetEntry in autoGroup.entries)
            {
                var objs = EditorUtility.CollectDependencies(new[] { assetEntry.MainAsset });
                var deps = objs.Select((o) => AssetDatabase.GetAssetPath(o))
                    .Distinct()
                    .Where((s) => !string.IsNullOrEmpty(s))
                    .Where((s) => !IGNORE_EXTS.Contains(Path.GetExtension(s)))
                    .Where((s) => s != "Library/unity default resources")
                    .ToList();
                foreach (var depPath in deps)
                {
                    Debug.Log($"[AutoGroup] process Shader {depPath}");
                    if (Path.GetExtension(depPath) != ".shader")
                    {
                        continue;
                    }

                    if (depPath.Contains("Editor"))
                    {
                        Debug.Log($"[AutoGroup] Editor Shader but continue: {depPath} {assetEntry.AssetPath}");
                        // continue;
                    }

                    var depGuid = AssetDatabase.AssetPathToGUID(depPath);
                    if (shaderGuidSet.Contains(depGuid))
                    {
                        continue;
                    }

                    shaderGuidSet.Add(depGuid);
                }
            }

            //将shader hashset 添加到资源列表 重置组
            foreach (var shaderGuid in shaderGuidSet)
            {
                var assetPath = AssetDatabase.GUIDToAssetPath(shaderGuid);
                var entry = settings.CreateOrMoveEntry(shaderGuid, autoGroup, false, false);
                entry.SetAddress(Path.GetFileName(assetPath).Replace(" ", ""), false);
                entry.labels.Clear();
                entry.SetLabel("all_shader", true, true, false);
            }
        }

        private static readonly string[] IGNORE_EXTS = { ".cs", ".js", ".boo", ".dll" };

        //private static readonly string[] IGNORE_Paths = { "/Editor/" };
        public static readonly string ShaderVariantsPath = "Assets/Art/Shaders/Variant/AllInOne.shadervariants";

        public static HashSet<string> GetShaderGuidSet(AddressableAssetSettings settings, AutoGroupSetting autoSettings)
        {
            var autoGroup = settings.groups.Find(g => string.Equals(g.Name, autoSettings._autoGroupName));

            HashSet<string> shaderGuidSet = new HashSet<string>();
            var svcGuid = AssetDatabase.AssetPathToGUID(ShaderVariantsPath);
            shaderGuidSet.Add(svcGuid);
            if (autoGroup != null)
            {
                foreach (var assetEntry in autoGroup.entries)
                {
                    if (Path.GetExtension(assetEntry.AssetPath) == ".shader")
                    {
                        shaderGuidSet.Add(assetEntry.guid);
                    }
                }
            }

            return shaderGuidSet;
        }

        public static void ReimportFolders(IEnumerable<string> assetPaths, AutoGroupSetting autoSettings,
            bool showConfirmDialog = true)
        {
            var pathsToImport = new HashSet<string>();
            foreach (var assetPath in assetPaths)
                if (Directory.Exists(assetPath))
                {
                    pathsToImport.Add(assetPath.Replace('\\', '/'));
                    var dirsToAdd = Directory.GetDirectories(assetPath, "*", SearchOption.AllDirectories);
                    foreach (var dir in dirsToAdd)
                        if (!dir.StartsWith(".") && !dir.EndsWith("~"))
                            pathsToImport.Add(dir.Replace('\\', '/'));
                    var filesToAdd = Directory.GetFiles(assetPath, "*", SearchOption.AllDirectories);
                    foreach (var file in filesToAdd)
                        if (!autoSettings.IsAssetIgnored(Path.GetFileName(file)))
                            pathsToImport.Add(file.Replace('\\', '/'));
                }

            if (pathsToImport.Count > 0)
            {
                Debug.Log($"[Auto Group]: Found {pathsToImport.Count} asset paths...");
                OnPostprocessAllAssets(pathsToImport.ToArray(), new string[0], new string[0], new string[0]);
            }
        }
    }
}